package org.jeecg.modules.iot.utils;

import org.jeecg.modules.iot.jtt808.JTT808Encoder;
import org.jeecg.modules.iot.jtt808.JTT808Message;
import org.jeecg.modules.iot.mqtt.entity.Packet;
import org.jeecg.modules.iot.model.modbus.ModbusParams;

import java.util.ArrayList;
import java.util.List;

import static org.jeecg.modules.iot.utils.JT808Utils.bytesToHexString;

public class ModbusRTUUtils {

/**
     * CRC16 校验码计算
     *
     * @param data 需要计算校验码的数据
     * @return CRC16 校验码*/


    public static int crc16(byte[] data) {
        int crc = 0xFFFF;
        for (byte b : data) {
            crc ^= (b & 0xFF) << 8;
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x8000) > 0) {
                    crc = (crc << 1) ^ 0x1021;
                } else {
                    crc <<= 1;
                }
            }
        }
        return crc & 0xFFFF;
    }

/**
     * 构建 Modbus-RTU 读取 Holding Register 的指令
     *
     * @param slaveAddress   从站地址
     * @param startAddress  起始寄存器地址
     * @param registerCount 读取寄存器数量
     * @return Modbus-RTU 数据帧*/


    public static byte[] buildReadHoldingRegistersRequest(int slaveAddress, int startAddress, int registerCount) {
        byte[] data = new byte[6];
        data[0] = (byte) slaveAddress;
        data[1] = 0x03; // 功能码：读取 Holding Register
        data[2] = (byte) (startAddress >> 8);
        data[3] = (byte) startAddress;
        data[4] = (byte) (registerCount >> 8);
        data[5] = (byte) registerCount;
        return calculateCRC16AndAppend(data,0,data.length);
    }
    private static byte[] calculateCRC16AndAppend(byte[] data, int start, int len) {
        int crc = 0xFFFF;
        for (int i = start; i < start + len; i++) {
            crc ^= (data[i] & 0xFF);
            for (int j = 0; j < 8; j++) {
                if ((crc & 0x0001) != 0) {
                    crc >>= 1;
                    crc ^= 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }

        // 创建一个新数组，长度为原始数据长度 + 2
        byte[] dataWithCRC = new byte[len + 2];

        // 复制原始数据到新数组
        System.arraycopy(data, start, dataWithCRC, 0, len);

        // 添加CRC校验码到新数组末尾
        dataWithCRC[len] = (byte) (crc & 0xFF);
        dataWithCRC[len + 1] = (byte) ((crc >> 8) & 0xFF);

        return dataWithCRC;
    }

    private static byte[] calculateCRC16(byte[] data, int start, int len) {
        int crc = 0xFFFF;
        for (int i = start; i < start + len; i++) {
            crc ^= (data[i] & 0xFF);
            for (int j = 0; j < 8; j++) {
                if ((crc & 0x0001) != 0) {
                    crc >>= 1;
                    crc ^= 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }
        return new byte[]{(byte) (crc & 0xFF), (byte) ((crc >> 8) & 0xFF)};
    }
/**
     * 构建 Modbus-RTU 写入多个 Holding Register 的指令
     *
     * @param slaveAddress 从站地址
     * @param startAddress 起始寄存器地址
     * @param values       要写入的值
     * @return Modbus-RTU 数据帧*/


    public static byte[] buildWriteMultipleRegistersRequest(int slaveAddress, int startAddress, byte[] values) {
        int registerCount = values.length/2;
        byte[] data = new byte[7 + registerCount * 2];
        data[0] = (byte) slaveAddress;
        data[1] = 0x10; // 功能码：写入多个 Holding Register
        data[2] = (byte) (startAddress >> 8);
        data[3] = (byte) startAddress;
        data[4] = (byte) (registerCount >> 8);
        data[5] = (byte) registerCount;
        data[6] = (byte) (registerCount * 2); // 写入字节数
        //注释原先的右移操作。不用设置高低位
for (int i = 0; i < registerCount; i++) {
            data[7 + i * 2] = (byte) (values[i] >>> 8);
            data[8 + i * 2] = (byte) values[i];
        }

        for (int i = 0; i < registerCount; i++) {
            data[7 + i * 2] = values[i * 2];     // 直接取高字节
            data[8 + i * 2] = values[i * 2 + 1]; // 直接取低字节
        }
        return calculateCRC16AndAppend(data,0,data.length);
    }

/**
     * 添加 CRC 校验码到数据帧末尾
     *
     * @param data 数据帧
     * @return 添加 CRC 校验码后的数据帧*/


    private static byte[] addCRC(byte[] data) {
        int crc = crc16(data);
        byte[] dataWithCRC = new byte[data.length + 2];
        System.arraycopy(data, 0, dataWithCRC, 0, data.length);
        dataWithCRC[data.length] = (byte) (crc & 0xFF);
        dataWithCRC[data.length + 1] = (byte) (crc >> 8);
        return dataWithCRC;
    }


    public static String generateModbusTransparentMessage(JTT808Message message, ModbusParams params, byte transparentMsgType, int messageId) {
        // ==== 使用 ModbusRTUUtils 和传入的参数生成透传数据 ====
        List<byte[]> transparentDataList = new ArrayList<>();

        // 根据传入的参数类型生成不同的 Modbus 指令
        switch (params.getFunctionCode()) {
            case READ_HOLDING_REGISTERS:
                byte[] readHoldingRegistersRequest = buildReadHoldingRegistersRequest(
                        params.getSlaveAddress(),
                        params.getStartAddress(),
                        params.getRegisterCount()
                );
                transparentDataList.add(readHoldingRegistersRequest);
                break;
            case WRITE_MULTIPLE_REGISTERS:
                byte[] writeMultipleRegistersRequest = buildWriteMultipleRegistersRequest(
                        params.getSlaveAddress(),
                        params.getStartAddress(),
                        params.getValues()
                );
                transparentDataList.add(writeMultipleRegistersRequest);
                break;
            // 添加其他 Modbus 功能码的处理逻辑
            default:
                throw new IllegalArgumentException("Unsupported Modbus function code: " + params.getFunctionCode());
        }
        // ==== 使用 ModbusRTUUtils 和传入的参数生成透传数据 ====


        message.packetCount = 0; // 设置分包总数，如果消息体超过1024字节需要分包发送
        message.packetIndex = 0; // 设置分包索引，表示当前分包的序号
        message.id = messageId; // 数据下行透传消息ID

        int totalDataLength = 0;
        for (byte[] transparentData : transparentDataList) {
            totalDataLength += transparentData.length + 1; // +1 是数据长度字节
        }

        Packet p = Packet.create(totalDataLength);
        p.addByte(transparentMsgType); // 添加透传消息类型

        // 添加多个透传数据
        for (byte[] transparentData : transparentDataList) {
            //p.addByte((byte) transparentData.length); // 添加数据长度
            p.addBytes(transparentData);             // 添加数据内容
        }

        message.body = p.getBytes();
        return bytesToHexString(JTT808Encoder.encode(message));
    }


}
